package com.szgz.platform.service;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.command.LogContainerResultCallback;
import com.github.dockerjava.okhttp.OkDockerHttpClient;
import lombok.extern.slf4j.Slf4j;
import com.szgz.platform.callback.DockerResultCallback;
import com.szgz.platform.util.MemoryParser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;

@Service
@Slf4j
public class ContainerOrchestrationService {

    @Value("${container.max-memory:512m}")
    private String maxMemory;

    @Value("${container.max-cpu:1}")
    private String maxCpu;

    @Value("${container.timeout:30}")
    private int timeoutSeconds;

    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
    private final Map<String, LanguageConfig> languageConfigs = initLanguageConfigs();
    private DockerClient dockerClient;

    /**
     * 语言配置类
     */
    private static class LanguageConfig {
        String image;
        String[] cmd;
        String[] extensions;
        Map<String, String> env;

        LanguageConfig(String image, String[] cmd, String[] extensions, Map<String, String> env) {
            this.image = image;
            this.cmd = cmd;
            this.extensions = extensions;
            this.env = env;
        }
    }


    /**
     * 创建 Docker 客户端
     */
    private DockerClient createDockerClient() {
        if (dockerClient == null) {
            try {
                // 使用 OkHttp 替代 Apache HttpClient
                DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();

                OkDockerHttpClient httpClient = new OkDockerHttpClient.Builder()
                        .dockerHost(config.getDockerHost())
                        .sslConfig(config.getSSLConfig())
                        .build();

                dockerClient = DockerClientBuilder.getInstance()
                        .withDockerHttpClient(httpClient)
                        .build();
            } catch (Exception e) {
                log.error("创建 Docker 客户端失败，使用默认方式", e);
                // 如果失败，回退到默认方式
                dockerClient = DockerClientBuilder.getInstance().build();
            }
        }
        return dockerClient;
    }


    /**
     * 初始化语言配置
     */
    private Map<String, LanguageConfig> initLanguageConfigs() {
        Map<String, LanguageConfig> configs = new HashMap<>();

        // Python配置
        Map<String, String> pythonEnv = new HashMap<>();
        pythonEnv.put("PYTHONUNBUFFERED", "1");
        configs.put("python", new LanguageConfig(
                "python:3.9-slim",
                new String[]{"python", "/code/main.py"},
                new String[]{".py"},
                pythonEnv
        ));

        // Java配置
        Map<String, String> javaEnv = new HashMap<>();
        javaEnv.put("JAVA_OPTS", "-Xmx256m");
        configs.put("java", new LanguageConfig(
                "openjdk:11-jdk-slim",
                new String[]{"sh", "-c", "cd /code && javac Main.java && java -cp /code Main"},
                new String[]{".java"},
                javaEnv
        ));

        // C++配置
        Map<String, String> cppEnv = new HashMap<>();
        configs.put("cpp", new LanguageConfig(
                "gcc:11",
                new String[]{"sh", "-c", "cp /code/main.cpp /tmp/ && cd /tmp && g++ -o main main.cpp && ./main"},
                new String[]{".cpp", ".c"},
                cppEnv
        ));

        // JavaScript配置
        Map<String, String> jsEnv = new HashMap<>();
        configs.put("javascript", new LanguageConfig(
                "node:16-slim",
                new String[]{"node", "/code/main.js"},
                new String[]{".js"},
                jsEnv
        ));

        // Go配置
        Map<String, String> goEnv = new HashMap<>();
        goEnv.put("GOPATH", "/go");
        goEnv.put("GOCACHE", "/tmp/go-cache");
        goEnv.put("GOTMPDIR", "/tmp/go-tmp");
        configs.put("go", new LanguageConfig(
                "golang:1.19-alpine",
                new String[]{"sh", "-c", "mkdir -p /tmp/go-cache /tmp/go-tmp && go run /code/main.go"},
                new String[]{".go"},
                goEnv
        ));

        // Rust配置
        Map<String, String> rustEnv = new HashMap<>();
        configs.put("rust", new LanguageConfig(
                "rust:1.75-slim",
                new String[]{"sh", "-c", "cd /code && cargo run --quiet --target-dir /tmp --release"},
                new String[]{".rs"},
                rustEnv
        ));

        return configs;
    }

    /**
     * 运行代码
     */
    public CodeExecutionResult runCode(String code, String language, String userId, String stdin) {
        LanguageConfig config = languageConfigs.get(language.toLowerCase());
        if (config == null) {
            return CodeExecutionResult.error("不支持的语言: " + language);
        }

        String containerId = null;
        String tempDir = null;

        try {
            // 创建临时目录
            tempDir = createTempDirectory(userId);

            // 创建代码文件
            String fileName = getFileName(language);
            File codeFile = new File(tempDir, fileName);
            writeCodeToFile(codeFile, code, language);

            // 创建容器
            containerId = createContainer(config, tempDir, userId, stdin, language);

            // 启动容器
            DockerClient dockerClient = createDockerClient();
            dockerClient.startContainerCmd(containerId).exec();

            // 等待执行完成
            CodeExecutionResult result = waitForExecution(dockerClient, containerId);

            return result;

        } catch (Exception e) {
            log.error("代码执行失败", e);
            return CodeExecutionResult.error("执行失败: " + e.getMessage());
        } finally {
            cleanup(containerId, tempDir);
        }
    }

    /**
     * 创建临时目录
     */
    private String createTempDirectory(String userId) throws IOException {
        String tempDir = System.getProperty("java.io.tmpdir") + File.separator +
                "code_" + userId + "_" + UUID.randomUUID();
        Files.createDirectories(Path.of(tempDir));
        return tempDir;
    }

    /**
     * 获取文件名
     */
    private String getFileName(String language) {
        switch (language.toLowerCase()) {
            case "python":
                return "main.py";
            case "java":
                return "Main.java";
            case "cpp":
                return "main.cpp";
            case "javascript":
                return "main.js";
            case "go":
                return "main.go";
            case "rust":
                return "main.rs";
            default:
                return "main";
        }
    }

    /**
     * 写入代码文件
     */
    private void writeCodeToFile(File file, String code, String language) throws IOException {
        if ("java".equalsIgnoreCase(language)) {
            // Java需要类名和文件名一致
            StringBuilder javaCode = new StringBuilder();
            javaCode.append("public class Main {\n");
            
            // 处理用户输入的代码，确保正确的缩进
            // 使用更安全的方式处理换行符和特殊字符
            String normalizedCode = code.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
            String[] lines = normalizedCode.split("\n");
            
            // 检查用户代码是否已经包含main方法
            boolean hasMainMethod = false;
            boolean hasClassDefinition = false;
            
            for (String line : lines) {
                String trimmedLine = line.trim();
                if (trimmedLine.startsWith("public static void main")) {
                    hasMainMethod = true;
                }
                if (trimmedLine.startsWith("public class") || trimmedLine.startsWith("class")) {
                    hasClassDefinition = true;
                }
            }
            
            if (hasClassDefinition) {
                // 如果用户代码包含类定义，直接使用用户代码
                code = normalizedCode;
            } else if (hasMainMethod) {
                // 如果用户代码包含main方法，包装成类
                javaCode.append("    ");
                for (String line : lines) {
                    String trimmedLine = line.trim();
                    if (!trimmedLine.isEmpty()) {
                        // 跳过包声明和import
                        if (trimmedLine.startsWith("package") || trimmedLine.startsWith("import")) {
                            continue;
                        }
                        javaCode.append("    ").append(trimmedLine).append("\n");
                    }
                }
                javaCode.append("}");
                code = javaCode.toString();
            } else {
                // 如果用户代码只是业务逻辑，包装成main方法
                javaCode.append("    public static void main(String[] args) {\n");
                
                for (String line : lines) {
                    String trimmedLine = line.trim();
                    if (!trimmedLine.isEmpty()) {
                        // 跳过包声明和import
                        if (trimmedLine.startsWith("package") || trimmedLine.startsWith("import")) {
                            continue;
                        }
                        
                        // 确保每行代码都是有效的Java语句
                        javaCode.append("        ").append(trimmedLine);
                        // 如果行末没有分号，自动添加
                        if (!trimmedLine.endsWith(";") && !trimmedLine.endsWith("}") && !trimmedLine.endsWith("{")) {
                            javaCode.append(";");
                        }
                        javaCode.append("\n");
                    }
                }
                
                javaCode.append("    }\n");
                javaCode.append("}");
                code = javaCode.toString();
            }
            
            // 添加调试日志
            log.info("生成的Java代码:\n{}", code);
        }

        // 为 Rust 创建完整的项目结构
        if ("rust".equalsIgnoreCase(language)) {
            createRustProject(file.getParentFile(), code);
        } else {
            try (FileWriter writer = new FileWriter(file)) {
                writer.write(code);
            }
        }
    }

    /**
     * 创建容器
     */
    private String createContainer(LanguageConfig config, String tempDir, String userId, String stdin, String language) {
        DockerClient dockerClient = createDockerClient();

        // 设置资源限制
        HostConfig hostConfig = HostConfig.newHostConfig()
                .withMemory(MemoryParser.parseToBytes(maxMemory))
                .withCpuCount(Long.parseLong(maxCpu))
                .withBinds(
                    new Bind(tempDir, new Volume("/code")),
                    new Bind(System.getProperty("java.io.tmpdir"), new Volume("/tmp"))
                )
                .withSecurityOpts(Arrays.asList("no-new-privileges"))
                .withReadonlyRootfs(true);

        // 设置环境变量
        List<String> envVars = new ArrayList<>();
        config.env.forEach((key, value) -> envVars.add(key + "=" + value));
        envVars.add("USER_ID=" + userId);

        CreateContainerResponse container = dockerClient.createContainerCmd(config.image)
                .withCmd(config.cmd)
                .withHostConfig(hostConfig)
                .withEnv(envVars)
                .withName("code_" + userId + "_" + UUID.randomUUID().toString().substring(0, 8))
                .exec();

        // 若有stdin，需要在启动后通过exec将输入写入进程（对需要交互输入的情况）
        if (stdin != null && !stdin.isEmpty()) {
            try {
                // 写入标准输入到文件，再在容器中通过重定向提供给进程
                // 这里创建一个stdin.txt
                Files.writeString(Path.of(tempDir, "stdin.txt"), stdin);
                // 修改命令为通过sh读取stdin.txt作为输入（仅对常见解释器适用，如 python/node 等用不到stdin时也不影响）
                // 针对python，替换cmd为：sh -c "python /code/main.py < /code/stdin.txt"
                if ("python".equalsIgnoreCase(language)) {
                    dockerClient.removeContainerCmd(container.getId()).withForce(true).exec();
                    String[] newCmd = new String[]{"sh", "-c", "python /code/main.py < /code/stdin.txt"};
                    container = dockerClient.createContainerCmd(config.image)
                            .withCmd(newCmd)
                            .withHostConfig(hostConfig)
                            .withEnv(envVars)
                            .withName("code_" + userId + "_" + UUID.randomUUID().toString().substring(0, 8))
                            .exec();
                }
            } catch (Exception e) {
                log.warn("准备stdin失败: {}", e.getMessage());
            }
        }

        return container.getId();
    }

    /**
     * 等待执行完成
     */
    private CodeExecutionResult waitForExecution(DockerClient dockerClient, String containerId) {
        try {
            // 设置超时
            Future<CodeExecutionResult> future = executorService.submit(() -> {
                try {
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    ByteArrayOutputStream errorStream = new ByteArrayOutputStream();

                    // 等待容器执行完成
                    DockerResultCallback callback = new DockerResultCallback();
                    dockerClient.waitContainerCmd(containerId).exec(callback);
                    callback.awaitCompletion(); // 关键：等待容器真正完成

                    // 读取容器日志
                    dockerClient.logContainerCmd(containerId)
                            .withStdOut(true)
                            .withStdErr(true)
                            .exec(new LogContainerResultCallback() {
                                @Override
                                public void onNext(Frame frame) {
                                    try {
                                        if (frame.getStreamType() == StreamType.STDOUT) {
                                            outputStream.write(frame.getPayload());
                                        } else {
                                            errorStream.write(frame.getPayload());
                                        }
                                    } catch (IOException e) {
                                        log.error("读取容器输出失败", e);
                                    }
                                }
                            }).awaitCompletion();

                    String output = outputStream.toString();
                    String error = errorStream.toString();

                    // 检查容器退出状态
                    InspectContainerResponse inspect = dockerClient.inspectContainerCmd(containerId).exec();
                    Integer exitCode = inspect.getState().getExitCode();

                    log.info("容器 {} 执行完成，退出码: {}, 输出: '{}', 错误: '{}'", 
                            containerId, exitCode, output, error);

                    if (exitCode != null && exitCode != 0) {
                        return CodeExecutionResult.error("执行错误 (退出码: " + exitCode + "): " + error);
                    }

                    return CodeExecutionResult.success(output);

                } catch (Exception e) {
                    return CodeExecutionResult.error("执行异常: " + e.getMessage());
                }
            });

            return future.get(timeoutSeconds, TimeUnit.SECONDS);

        } catch (TimeoutException e) {
            return CodeExecutionResult.error("执行超时");
        } catch (Exception e) {
            return CodeExecutionResult.error("执行失败: " + e.getMessage());
        }
    }

    /**
     * 清理资源
     */
    private void cleanup(String containerId, String tempDir) {
        try {
            if (containerId != null) {
                DockerClient dockerClient = createDockerClient();
                dockerClient.removeContainerCmd(containerId).withForce(true).exec();
            }
        } catch (Exception e) {
            log.error("清理容器失败", e);
        }

        try {
            if (tempDir != null) {
                Files.walk(Path.of(tempDir))
                        .map(Path::toFile)
                        .sorted((o1, o2) -> -o1.compareTo(o2))
                        .forEach(File::delete);
            }
        } catch (Exception e) {
            log.error("清理临时目录失败", e);
        }
    }

    /**
     * 获取支持的语言列表
     */
    public List<String> getSupportedLanguages() {
        return new ArrayList<>(languageConfigs.keySet());
    }

    /**
     * 代码执行结果
     */
    public static class CodeExecutionResult {
        private final boolean success;
        private final String output;
        private final String error;

        private CodeExecutionResult(boolean success, String output, String error) {
            this.success = success;
            this.output = output;
            this.error = error;
        }

        public static CodeExecutionResult success(String output) {
            return new CodeExecutionResult(true, output, null);
        }

        public static CodeExecutionResult error(String error) {
            return new CodeExecutionResult(false, null, error);
        }

        public boolean isSuccess() {
            return success;
        }

        public String getOutput() {
            return output;
        }

        public String getError() {
            return error;
        }
    }

    /**
     * 为 Rust 创建完整的项目结构
     */
    private void createRustProject(File projectDir, String code) throws IOException {
        // 创建 Cargo.toml 文件
        File cargoToml = new File(projectDir, "Cargo.toml");
        try (FileWriter writer = new FileWriter(cargoToml)) {
            writer.write("[package]\n");
            writer.write("name = \"rust_code\"\n");
            writer.write("version = \"0.1.0\"\n");
            writer.write("edition = \"2021\"\n");
            writer.write("\n");
            writer.write("[dependencies]\n");
        }

        // 创建 src 目录
        File srcDir = new File(projectDir, "src");
        srcDir.mkdirs();

        // 创建 main.rs 文件
        File mainRs = new File(srcDir, "main.rs");
        try (FileWriter writer = new FileWriter(mainRs)) {
            writer.write(code);
        }

        log.info("Rust 项目结构创建完成: {}", projectDir.getAbsolutePath());
    }
} 